package com.xlh.virtualization.controller;

import com.xlh.common.Result;
import com.xlh.security.UserInfoHolder;
import com.xlh.virtualization.domain.ContainerContext;
import com.xlh.virtualization.domain.ExperimentContainer;
import com.xlh.virtualization.domain.ExperimentContext;
import com.xlh.virtualization.service.ContainerService;
import com.xlh.virtualization.service.ExperimentService;
import com.xlh.virtualization.vo.ExperimentContainerVO;
import com.xlh.virtualization.vo.ExperimentVO;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.xlh.virtualization.constant.ContainerRoleEnum.SINGLE;
import static com.xlh.virtualization.constant.ContainerRoleEnum.SLAVE;
import static com.xlh.virtualization.constant.ExperimentConstant.CLUSTER;
import static com.xlh.virtualization.constant.ExperimentConstant.SINGLE_NODE;

/**
 * @author cheer
 */
public abstract class BaseExperimentController {

    protected UserInfoHolder userInfoHolder;

    protected ExperimentService experimentService;

    protected ContainerService containerService;

    public Result<ExperimentVO> enterExperiment(Long contextId) {
        // 获取实验环境的类型和资源id
        ContainerContext containerContext = getContainerContext(userInfoHolder.getUserId(), contextId);

        ExperimentContext experimentContext = getExperimentContext(userInfoHolder.getUserId(), contextId);

        // 进入实验前处理逻辑
        preEnterExperiment(containerContext, experimentContext);

        // 获取实验容器
        List<ExperimentContainer> experimentContainers = getExperimentContainers(containerContext, experimentContext);

        // 删除无效容器
        containerService.deleteExtraContainerByContext(containerContext,
                experimentContainers.stream().map(ExperimentContainer::getContainerId).collect(Collectors.toList()));

        // 进入实验后处理逻辑
        postEnterExperiment(containerContext, experimentContext, experimentContainers);

        // 转换为前端需要的格式
        return buildExperimentVO(experimentContainers);
    }

    public Result<ExperimentVO> initExperiment(Long contextId) {
        // 获取实验环境的类型和资源id
        ContainerContext containerContext = getContainerContext(userInfoHolder.getUserId(), contextId);

        ExperimentContext experimentContext = getExperimentContext(userInfoHolder.getUserId(), contextId);

        // 进入实验前处理逻辑
        preEnterExperiment(containerContext, experimentContext);

        // 删除当前的容器
        containerService.deleteContainerByContext(containerContext);

        // 获取实验容器
        List<ExperimentContainer> experimentContainers = getExperimentContainers(containerContext, experimentContext);

        // 进入实验后处理逻辑
        postEnterExperiment(containerContext, experimentContext, experimentContainers);

        // 转换为前端需要的格式
        return buildExperimentVO(experimentContainers);
    }

    private List<ExperimentContainer> getExperimentContainers(ContainerContext containerContext, ExperimentContext experimentContext) {
        try {
            return experimentService.getExperiment(containerContext, experimentContext, getVolumePath(containerContext));
        } catch (Exception e) {
            containerService.stopContainerByContext(containerContext);
            handleEnterExperimentException(containerContext, experimentContext);
            throw e;
        }
    }

    private Result<ExperimentVO> buildExperimentVO(List<ExperimentContainer> experimentContainers) {
        List<ExperimentContainerVO> experimentContainerVOS = new ArrayList<>();
        for (ExperimentContainer experimentContainer : experimentContainers) {
            ExperimentContainerVO experimentContainerVO = ExperimentContainerVO.builder()
                    .containerId(experimentContainer.getContainerId())
                    .containerRole(SLAVE == experimentContainer.getContainerRole() ?
                            experimentContainer.getContainerRole().getMessage() + experimentContainer.getRoleNumber() :
                            experimentContainer.getContainerRole().getMessage())
                    .imageName(experimentContainer.getImageName())
                    .ip(experimentContainer.getIp())
                    .flavor(String.format("CPU上限%s核  内存上限%sG", experimentContainer.getCpu(),
                            experimentContainer.getRam()))
                    .desktopUrl(experimentContainer.getDesktopUrl())
                    .fullScreen(experimentContainer.getFullScreen())
                    .build();
            experimentContainerVOS.add(experimentContainerVO);
        }
        ExperimentVO experimentVO = ExperimentVO.builder()
                .mode(SINGLE == experimentContainers.get(0).getContainerRole() ? SINGLE_NODE : CLUSTER)
                .containers(experimentContainerVOS)
                .build();
        return Result.success(experimentVO);
    }

    protected abstract ContainerContext getContainerContext(Long userId, Long contextId);

    protected abstract ExperimentContext getExperimentContext(Long userId, Long contextId);

    protected void preEnterExperiment(ContainerContext containerContext, ExperimentContext experimentContext) {
    }

    protected void postEnterExperiment(ContainerContext containerContext, ExperimentContext experimentContext,
                                       List<ExperimentContainer> experimentContainers) {
    }

    protected void handleEnterExperimentException(ContainerContext containerContext, ExperimentContext experimentContext) {
    }

    protected String getVolumePath(ContainerContext containerContext) {
        return null;
    }
}
